﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;


public delegate void EventHandler (string tag, params object[] args);

public class EventManager
{
    public static EventManager SP { get; internal set; }

    private Dictionary<string, List<EventEntry>> tagMaps;

    private Dictionary<object, List<EventEntry>> targetMaps;

    private List<EventEntry> removeList;

    private bool isRemoveDirty;

    internal EventManager()
    {
        tagMaps = new Dictionary<string, List<EventEntry>> ();
        targetMaps = new Dictionary<object, List<EventEntry>> ();
        removeList = new List<EventEntry> ();
        isRemoveDirty = false;
    }

    public void AddEvent(string tag, EventHandler handler, object obj)
    {
        List<EventEntry> list;

        if (!targetMaps.TryGetValue(obj, out list))
        {
            list = new List<EventEntry> ();
            targetMaps.Add (targetMaps, list);
        }

        EventEntry entry = findTagToEntry (list, tag);
        if (entry != null)
        {
            if (entry.isDirty)
            {
                entry.isDirty = false;
                removeList.Remove (entry);
            }
            return;
        }

        entry = addListenerMap (tag, handler, obj);
        list.Add (entry);
    }

    public void ExeEvent(string tag, params object[] args)
    {
        List<EventEntry> list;
        if (!tagMaps.TryGetValue (tag, out list)) return;

        int len = list.Count;
        EventEntry entry;
        while(--len >= 0)
        {
            entry = list[len];
            if (entry.isDirty) continue;
            entry.Execute (args);
        }
    }

    public void DelEvent(string tag, object obj)
    {
        List<EventEntry> list;
        if (!targetMaps.TryGetValue (obj, out list)) return;

        EventEntry entry = findTagToEntry (list, tag);
        if (entry == null || entry.isDirty) return;

        entry.isDirty = true;
        removeList.Add (entry);
        isRemoveDirty = true;
    }

    public void DelAllEvent(object obj)
    {
        List<EventEntry> list;
        if (!targetMaps.TryGetValue (obj, out list)) return;

        int len = list.Count;
        if (len == 0) return;

        EventEntry entry;
        while(--len >= 0)
        {
            entry = list[len];
            if (entry.isDirty) continue;
            entry.isDirty = true;
            removeList.Add (entry);
        }

        isRemoveDirty = true;
    }
    
    private EventEntry findTagToEntry(List<EventEntry> arr, string tag)
    {
        int len = arr.Count;
        while(--len >= 0)
        {
            if (tag == arr[len].tag) return arr[len];
        }

        return null;
    }

    private EventEntry addListenerMap(string tag, EventHandler handler, object target)
    {
        List<EventEntry> entrylist;

        if (tagMaps.TryGetValue(tag, out entrylist))
        {
            entrylist = new List<EventEntry> ();
            tagMaps.Add (tag, entrylist);
        }

        var entry = new EventEntry ();
        entry.Init (tag, target, handler);
        entrylist.Add (entry);
        return entry;
    }



    private List<EventEntry> runTagList;
    private List<EventEntry> runTargetList;
    private EventEntry runEntry;
    // 当isRemoveDirty为true时 立马删除 不让再调用 
    public void Update()
    {
        if (isRemoveDirty)
        {
            int size = 0;
            int len = removeList.Count;

            while(--len >= 0)
            {
                runEntry = removeList[len];
                runTagList = null;

                if (tagMaps.TryGetValue (runEntry.tag, out runTagList))
                {
                    runTargetList = null;
                    if (!targetMaps.TryGetValue (runEntry.target, out runTargetList)) continue;

                    size = runTargetList.Count;
                    arrRemoveItem (runTagList, runEntry, runTagList.Count);
                    arrRemoveItem (runTargetList, runEntry, size);

                    if (1 == size) targetMaps.Remove (runEntry);

                    runEntry.Destroy ();
                }

                removeList.Clear ();
                isRemoveDirty = false;
                runTagList = null;
                runTargetList = null;
                runEntry = null;
            }
        }
    }

    private void arrRemoveItem(List<EventEntry> arr, EventEntry item, int len)
    {
        if (1 == len) { arr.RemoveAt (0); }
        else
        {
            int index = arr.IndexOf (item);
            if (index == -1) return;

            len = len - 1;
            if (len == index) { arr.RemoveAt (index); }
            else
            {
                arr[index] = arr[len];
                arr.RemoveAt (len);
            }
        }
    }
}
